package deployr.core;

import static deployr.core.Checksums.*;
import static deployr.core.Streams.*;

import java.io.*;
import java.math.*;
import java.security.*;
import java.util.*;

public class ChecksumBuilder {

	private Iterable<String> files;

	private String rootPath;

	private final Map<String, String> indexMd5 = new TreeMap<String, String>();

	private MessageDigest digest = usingMd5();

	public ChecksumBuilder(MessageDigest digest) {
		this.files = new LinkedList<String>();
		this.rootPath = "/";
		this.digest = digest;
	}

	public ChecksumBuilder(String rootPath, Iterable<String> files) {
		this(rootPath, files, usingMd5());
	}

	public ChecksumBuilder(String rootPath, Iterable<String> files, MessageDigest digest) {
		this.rootPath = rootPath;
		this.files = files;
		this.digest = digest;
	}

	public ChecksumBuilder(String checksum) {
		Scanner scanner = new Scanner(checksum).useDelimiter("\n");
		while (scanner.hasNext()) {
			String string = scanner.next();
			add(string);
		}
	}

	public void add(String string) {
		String path = string.replaceAll("^.* ", "");
		String hash = string.replaceAll(" .*$", "");
		indexMd5.put(path, hash);
	}

	public ChecksumBuilder() {}

	public Checksum checksum() {
		return new Checksum(indexMd5);
	}

	public String checksum(File file) {
		try {
			InputStream inputStream = openInputStreamOn(file);
			final byte[] buf = new byte[8192];
			int len;
			while ((len = inputStream.read(buf)) > 0) {
				digest.update(buf, 0, len);
			}
			inputStream.close();
			return new BigInteger(1, digest.digest()).toString(16);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public Checksum checksum(String outFilename) {
		if (thereArentFiles()) return new Checksum();

		for (String relativePath : files) {
			File file = file(relativePath);
			if (isNot(file)) continue;
			indexMd5.put(relativePath, checksum(file));
		}

		return checksum();

	}

	private File file(String relativePath) {
		return new File(rootPath, relativePath);
	}

	private boolean isNot(File file) {
		return file.isDirectory();
	}

	private boolean thereArentFiles() {
		return !files.iterator().hasNext();
	}

}